﻿using Microsoft.Extensions.Logging;
using Sgr.Caching.Services;
using Sgr.Domain.Entities;
using Sgr.Organizations;
using Sgr.UPMS.Application.Queries;
using Sgr.UPMS.Application.ViewModels;
using Sgr.UPMS.Domain.Departments;
using Sgr.UPMS.Domain.Roles;
using Sgr.UPMS.Domain.Users;
using Sgr.Utilities;

namespace Sgr.UPMS.Application.Services
{
    public class UserDataCacheService : IUserProvider
    {
        private readonly ICacheManager _cacheManager;
        private readonly IUpmsQueryContext _context;
        private readonly IUserPermissionQueries _userPermissionQueries;
        private readonly ILogger<OrgDataCacheService> _logger;

        public UserDataCacheService(
            ICacheManager cacheManager,
            IUserPermissionQueries userPermissionQueries,
            IUpmsQueryContext context,
            ILogger<OrgDataCacheService> logger)
        {
            _cacheManager = cacheManager ?? throw new ArgumentNullException(nameof(cacheManager));
            _userPermissionQueries = userPermissionQueries ?? throw new ArgumentNullException(nameof(userPermissionQueries));
            _context = context ?? throw new ArgumentNullException(nameof(context));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        #region IUserProvider

        public async Task<UserInfo?> GetByIdAsync(long orgId, long userId, CancellationToken ct = default)
        {
            return await GetByKeyAsync(orgId, userId, ct);
        }

        public async Task<UserInfo?> GetByEmailAsync(string email, CancellationToken ct = default)
        {
            if (string.IsNullOrWhiteSpace(email))
                return null;

            // 规范化邮箱并计算哈希（用于缓存键）
            var normalizedEmail = email.Trim().ToLowerInvariant();
            var user = await _context.FirstOrDefaultAsync(
                _context.Query<User>()
                    .Where(u => u.UserEmail == normalizedEmail)
                    .Select(u => new { u.OrgId, u.Id }), ct);

            if (user == null || user.OrgId <= 0 || user.Id <= 0)
                return null;

            // 委托到按 id 的单一事实源缓存（若未命中会从 DB 加载并回填）
            return await GetByKeyAsync(user.OrgId, user.Id, ct);
        }

        public async Task<UserInfo?> GetByMobileAsync(string mobile, CancellationToken ct = default)
        {
            if (string.IsNullOrWhiteSpace(mobile))
                return null;

            var user = await _context.FirstOrDefaultAsync(
                _context.Query<User>()
                    .Where(u => u.UserPhone == mobile)
                    .Select(u => new { u.OrgId, u.Id }), ct);

            if (user == null || user.OrgId <= 0 || user.Id <= 0)
                return null;

            // 委托到按 id 的单一事实源缓存（若未命中会从 DB 加载并回填）
            return await GetByKeyAsync(user.OrgId, user.Id, ct);
        }

        public async Task<IReadOnlyList<UserInfo>> GetDeptManagerUserAsync(string orgId, string deptId, int max, CancellationToken ct = default)
        {
            if (string.IsNullOrWhiteSpace(deptId) || max <= 0)
                return Array.Empty<UserInfo>();

            if (long.TryParse(orgId, out var orgIdLong) == false || orgIdLong <= 0)
                return Array.Empty<UserInfo>();

            if (long.TryParse(deptId, out var deptIdLong) == false || deptIdLong <= 0)
                return Array.Empty<UserInfo>();

            var uid = await _cacheManager.GetAsync<ValueTuple<long, long?>?>(
                CacheKeys.GetDeptManagerDeptKey(orgId, deptId),
                async () =>
                {
                    try
                    {
                        // 只查询主管 Id（避免拉取不必要列）
                        var deptRow = await _context.FirstOrDefaultAsync(
                            _context.Query<Department>()
                                .Where(d => d.OrgId == orgIdLong && d.Id == deptIdLong && d.State == EntityStates.Normal)
                                .Select(u => new ValueTuple<long, long?>(u.OrgId, u.ManagerUserId)), ct);

                        return deptRow;
                    }
                    catch (OperationCanceledException)
                    {
                        throw;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "Error getting department manager user info for deptId: {deptId}", deptId);
                        return null;
                    }
                },
                CacheKeys.CreateUserFromDepManagerCacheOptions(),
                ct);

            if (!uid.HasValue || uid.Value.Item1 <= 0 || !uid.Value.Item2.HasValue || uid.Value.Item2.Value <= 0)
                return Array.Empty<UserInfo>();

            var manager = await GetByKeyAsync(uid.Value.Item1, uid.Value.Item2.Value, ct);
            if (manager == null)
                return Array.Empty<UserInfo>();

            // 部门主管通常为单人：返回至多 1 条；若未来支持多主管请调整 LoadByDeptManagerAsync 返回集合并在此按 max 截断
            return new List<UserInfo> { manager };
        }

        public async Task<bool> CheckUserIsDeptManager(string orgId, string deptId, string userId, CancellationToken ct = default)
        {
            var items = await GetDeptManagerUserAsync(orgId, deptId, 100, ct);

            return items.Count(f => f.UserId.ToString() == userId) == 1;
        }

        public async Task<IReadOnlyList<UserInfo>> GetByPermissionAsync(string orgId, string permission, int max, CancellationToken ct = default)
        {
            if (string.IsNullOrWhiteSpace(orgId) || string.IsNullOrWhiteSpace(permission) || max <= 0)
                return Array.Empty<UserInfo>();

            if (long.TryParse(orgId, out var orgIdLong) == false || orgIdLong <= 0)
                return Array.Empty<UserInfo>();

            // 采用“短 TTL（自动失效）” 缓存策略，避免权限变更后长时间缓存不更新的问题
            // 1. 注意使用缓存后，在审批执行时做最终权限复核（在 Approve/Reject Handler 再次校验用户是否仍有权限）
            // 2. 默认缓存时间较短（如 5 分钟），当查询数据结果为空时使用更短时间（如 30秒）缓存，防止缓存穿透
            var list = await _cacheManager.GetAsync(
                CacheKeys.GetUserPermissionKey(orgId, permission),
                async () =>
                {
                    try
                    {
                        var list = await LoadByPermissionAsync(orgIdLong, permission, max, ct);
                        return list.Count > 0 ? list : null;
                    }
                    catch (OperationCanceledException)
                    {
                        throw;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "Error getting  user info for permission: {permission} org: {orgId}", permission, orgId);
                        return null;
                    }
                },
                CacheKeys.CreateUserFromPermissionCacheOptions(),
                ct);

            return list ?? Array.Empty<UserInfo>();
        }

        #endregion IUserProvider

        private async Task<UserInfo?> GetByKeyAsync(long orgId, long userId, CancellationToken ct = default)
        {
            if (userId <= 0)
                throw new ArgumentException("Invalid userId", nameof(userId));

            var user = await _userPermissionQueries.GetUserPermissionAsync(orgId, userId, ct);

            if (user == null)
            {
                _logger.LogWarning("User not found for userId: {userId}", userId);
                return null;
            }

            var departmentIds = user.DepartmentId.HasValue
                ? new List<string> { user.DepartmentId.Value.ToString() }
                : new List<string>();

            return new UserInfo
            {
                UserId = user.UserId,
                RealName = user.RealName ?? string.Empty,
                //Email = user.UserEmail,
                //Mobile = user.UserPhone,
                DepartmentIds = departmentIds,
                IsActive = true,
                LastModified = null,
                Source = "UPMS",
                ExternalId = user.UserId.ToString()
            };
        }

        private async Task<IReadOnlyList<UserInfo>> LoadByPermissionAsync(long orgId, string permission, int max, CancellationToken ct = default)
        {
            // 先筛选出满足权限的 roleId 子查询（更清晰、通常更易被翻译为子查询/IN）
            var rolesWithPermissionQuery = _context.Query<Role>()
                .Where(r => r.State == EntityStates.Normal && r.Resources.Any(res => res.ResourceType == ResourceType.FunctionalPermission && res.ResourceCode == permission))
                .Select(r => r.Id);

            // 再在用户表中匹配这些 roleId
            var usersQuery = _context.Query<User>()
                .Where(u => u.OrgId == orgId && u.State == EntityStates.Normal && u.Roles.Any(ur => rolesWithPermissionQuery.Contains(ur.RoleId)))
                .Select(u => new
                {
                    u.Id,
                    u.UserName,
                    u.UserEmail,
                    u.UserPhone,
                    u.DepartmentId,
                    u.State,
                    u.LastModificationTime
                })
                .OrderByDescending(u => u.LastModificationTime)
                .ThenBy(u => u.Id) // 保持稳定排序
                .Take(max);

            //// 单条 SQL：通过子查询关联角色资源与用户角色，避免在客户端拉回大量 roleIds
            //var usersQuery = _context.Query<User>()
            //    .Where(u => u.State == EntityStates.Normal
            //                && u.Roles.Any(ur =>
            //                    _context.Query<Role>().Any(r => r.Id == ur.RoleId
            //                        && r.OrgId == orgId &&
            //                        && r.State == EntityStates.Normal
            //                        && r.Resources.Any(res => res.ResourceType == ResourceType.FunctionalPermission && res.ResourceCode == permission))))
            //    .Select(u => new
            //    {
            //        u.Id,
            //        u.UserName,
            //        u.UserEmail,
            //        u.UserPhone,
            //        u.DepartmentId,
            //        u.State,
            //        u.LastModificationTime
            //    })
            //    .OrderByDescending(u => u.LastModificationTime)
            //    .Take(max);

            var users = (await _context.ToListAsync(usersQuery, ct)).ToList();

            var result = users.Select(u => new UserInfo
            {
                UserId = u.Id,
                RealName = u.UserName ?? string.Empty,
                Email = u.UserEmail,
                Mobile = u.UserPhone,
                DepartmentIds = u.DepartmentId.HasValue ? new List<string> { u.DepartmentId.Value.ToString() } : new List<string>(),
                IsActive = u.State == EntityStates.Normal,
                LastModified = u.LastModificationTime,
                Source = "UPMS",
                ExternalId = u.Id.ToString()
            }).ToList();

            return result;
        }

        //private async Task<UserInfo?> LoadUserAsync(long userId, CancellationToken cancellationToken)
        //{
        //    _logger.LogDebug("Getting user info from database for userId: {userId}", userId);

        //    var query = _context.Query<User>()
        //        .Where(o => o.Id == userId)
        //        .Select(f => new { f.Id, f.UserName, f.UserEmail, f.UserPhone, f.DepartmentId, f.State, f.LastModificationTime });

        //    var user = await _context.FirstOrDefaultAsync(query, cancellationToken);

        //    if (user == null)
        //    {
        //        _logger.LogWarning("User not found for userId: {userId}", userId);
        //        return null;
        //    }

        //    var departmentIds = user.DepartmentId.HasValue
        //        ? new List<string> { user.DepartmentId.Value.ToString() }
        //        : new List<string>();

        //    return new UserInfo
        //    {
        //        UserId = user.Id,
        //        UserName = user.UserName ?? string.Empty,
        //        Email = user.UserEmail,
        //        Mobile = user.UserPhone,
        //        DepartmentIds = departmentIds,
        //        IsActive = user.State == EntityStates.Normal,
        //        LastModified = user.LastModificationTime,
        //        Source = "UPMS",
        //        ExternalId = user.Id.ToString()
        //    };
        //}
    }
}